﻿#include "FITKGraphObjectBC.h"

// VTK
#include <vtkMapper.h>
#include <vtkArrowSource.h>
#include <vtkConeSource.h>
#include <vtkPoints.h>
#include <vtkVertex.h>
#include <vtkCellArray.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkIntArray.h>
#include <vtkDoubleArray.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkMaskPoints.h>
#include <vtkProperty.h>
#include <vtkSelectionNode.h>
#include <vtkSelection.h>
#include <vtkExtractSelection.h>
#include <vtkIdTypeArray.h>
#include <vtkUnstructuredGrid.h>
#include <vtkAppendFilter.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKGraphCommons.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaBoundaryConditionDisplacement.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaBoundaryConditionSymmetry.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaBoundaryConditionVelocity.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaModelInfo.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaRefPoint.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaAssembly.h"
#include "FITK_Interface/FITKInterfaceModel/FITKModelSet.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractAssInstance.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"
#include "FITK_Interface/FITKInterfaceModel/FITKCoordinateSystem.h"

namespace Exchange
{
    /**
     * @brief   Internal enum for actor and poly data index.
     * @author  ChengHaotian (yeguangbaozi@foxmail.com)
     * @date    2024-07-01
     */
    enum _DataIndex
    {
        // The data one single glyph with none-zero value.
        _NoneZeroSingle = 0,

        // The data one single glyph with zero value.
        _ZeroSingle,

        // The data two glyphs with none-zero value.
        _NoneZeroDouble,

        // The data two glyphs with zero value.
        _ZeroDouble,

        // The end of index.
        _DataCount,

        // The second glyph for double glyph data.
        _NoneZeroDouble2 = _DataCount,
        _ZeroDouble2,

        // The end of the actor index.
        _ActorCount
    };

    FITKGraphObjectBC::FITKGraphObjectBC(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaBoundaryConditionDisplacement* bcData, int stepId)
        : FITKGraphObjectNodeLoadBase(caseData, bcData, stepId)
    {
        if (!bcData)
        {
            return;
        }

        // Initialize the source and colors.
        initSource();
        m_color_1 = FITKGraphCommons::s_dispBCColor;
        m_color_2 = FITKGraphCommons::s_rotateBCColor;

        // Get the value of current step.
        getValue(m_stepId);

        // Generate the BC's node set poly data.
        bool flag = generatePolyData(bcData);
        if (!flag)
        {
            return;
        }

        // Generate the BC actors.
        generateGraphs();

        // Save the graph information.
        m_graphInfo.Type = OtherGraph;

        // Update the visibility.
        updateVisibility();
    }

    FITKGraphObjectBC::FITKGraphObjectBC(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaBoundaryConditionSymmetry* bcData, int stepId)
        : FITKGraphObjectNodeLoadBase(caseData, bcData, stepId)
    {
        if (!bcData)
        {
            return;
        }

        // Initialize the source and colors.
        initSource();
        m_color_1 = FITKGraphCommons::s_dispBCColor;
        m_color_2 = FITKGraphCommons::s_rotateBCColor;

        // Get the value of current step.
        getValue(m_stepId);

        // Generate the BC's node set poly data.
        bool flag = generatePolyData(bcData);
        if (!flag)
        {
            return;
        }

        // Generate the BC actors.
        generateGraphs();

        // Save the graph information.
        m_graphInfo.Type = OtherGraph;

        // Update the visibility.
        updateVisibility();
    }

    FITKGraphObjectBC::FITKGraphObjectBC(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaBoundaryConditionVelocity* bcData, int stepId)
        : FITKGraphObjectNodeLoadBase(caseData, bcData, stepId)
    {
        if (!bcData)
        {
            return;
        }

        // Initialize the source and colors.
        initSource();
        m_color_1 = FITKGraphCommons::s_velBCColor;
        m_color_2 = FITKGraphCommons::s_angleVelBCColor;

        // Get the value of current step.
        getValue(m_stepId);

        // Generate the BC's node set poly data.
        bool flag = generatePolyData(bcData);
        if (!flag)
        {
            return;
        }

        // Generate the BC actors.
        generateGraphs();

        // Save the graph information.
        m_graphInfo.Type = OtherGraph;

        // Update the visibility.
        updateVisibility();
    }

    FITKGraphObjectBC::~FITKGraphObjectBC()
    {
        // Clear all data.
        clearCache();
    }

    void FITKGraphObjectBC::disHighlight()
    {
        if (m_actorList.count() < 6)
        {
            return;
        }

        initActorProperties(m_loadActorList[_ZeroSingle], 1);
        initActorProperties(m_loadActorList[_NoneZeroSingle], 1);
        initActorProperties(m_loadActorList[_ZeroDouble], 2);
        initActorProperties(m_loadActorList[_NoneZeroDouble], 2);
        initActorProperties(m_loadActorList[_ZeroDouble2], 2);
        initActorProperties(m_loadActorList[_NoneZeroDouble2], 2);

        FITKGraphObject3D::disHighlight();
    }

    void FITKGraphObjectBC::setVisible(bool visibility)
    {
        if (visibility)
        {
            showActors();
        }
        else
        {
            for (FITKGraphActor* fActor : m_loadActorList)
            {
                if (fActor)
                {
                    fActor->SetVisibility(false);
                }
            }

            // FITKGraphObject3D::setVisible(false);
        }
    }

    void FITKGraphObjectBC::clearCache()
    {
        // Delete the additional data.
        deleteVtkObjs(m_polyDataList);
        deleteVtkObjs(m_pointsList);
        deleteVtkObjs(m_cellsList);
        deleteVtkObjs(m_normalList);
        deleteVtkObjs(m_scalarList);
        deleteVtkObjs(m_idArrayList);
        deleteVtkObjs(m_modelIdArrayList);
        
        // Delete all extractors.
        deleteVtkObjs(m_selectNodeList);
        deleteVtkObjs(m_sectionList);
        deleteVtkObjs(m_extractSelectionList);

        // The sources will be deleted in base class's 'm_sourceList', so just give a nullptr.
        m_arrowSource = nullptr;
        m_coneSource = nullptr;

        // Clear poly data.
        FITKGraphObjectLoadBase::clearCache();
    }

    void FITKGraphObjectBC::update(bool forceUpdate)
    {
        Interface::FITKBoundaryLocation* bc = dynamic_cast<Interface::FITKBoundaryLocation*>(_dataObj);
        if (!bc)
        {
            return;
        }

        // Clear data, the cells must be deleted, or will cause memory leak.( For value or set changed )
        deleteVtkObjs(m_cellsList);
        for (int i = 0; i < _DataCount; i++)
        {
            resetVtkObj(m_pointsList[i]);
            m_cellsList.push_back(vtkCellArray::New());
            resetVtkObj(m_normalList[i]);
            resetVtkObj(m_scalarList[i]);
        }

        // Get set detail information.
        AbaqusData::AbaModelInfo requiredData = AbaqusData::FITKAbaModelInfo::GetModelInfo(bc);

        // The set must be not a nullptr.
        if (requiredData._set)
        {
            // Get the set id.
            int setId = requiredData._set->getDataObjectID();

            // Clear data.( For set changed )
            if (setId != m_setId || forceUpdate)
            {
                // Clear data.
                clearData();

                bool flag = generatePolyData(bc);
                if (!flag)
                {
                    return;
                }
            }
        }
        else
        {
            // The reason of set is nullptr: The set is deleted.
            clearData();
        }

        // Get the new value.
        getValue(m_stepId);

        // Add data again.
        int nPoints = m_loadPoints->GetNumberOfPoints();
        for (int i = 0; i < nPoints; i++)
        {
            double* pos = m_loadPoints->GetPoint(i);
            insertNextPoint(pos, -1, false);
        }

        // Update the visibility.
        updateVisibility();
        // showActors();
    }

    void FITKGraphObjectBC::updateVisibility()
    {
        // Update the full actor's visibility.
        FITKGraphObject3D::updateVisibility();

        QHash<int, bool> modelVisibilityHash;

        // Save the visibility first, and check if the visbility has changed at the same time.
        bool hasChanged = false;
        for (int modelId : m_modelIds)
        {
            bool parentVisible = getParentDataVisibility(modelId);
            if (modelId == -1)
            {
                parentVisible = true;
            }

            bool oldVisibility = false;
            if (m_modelVisibilityHash.contains(modelId))
            {
                oldVisibility = m_modelVisibilityHash[modelId];
            }

            if (parentVisible != oldVisibility)
            {
                hasChanged = true;
            }

            modelVisibilityHash.insert(modelId, parentVisible);
        }

        // If the visibility hasn't changed, then return.
        if (!hasChanged && !m_modelVisibilityHash.isEmpty())
        {
            return;
        }

        // Reset the extractor's ids.
        for (vtkIdTypeArray* idArray : m_idArrayList)
        {
            resetVtkObj(idArray);
        }

        // Update the extractor's ids.
        for(int i = 0 ; i < _DataCount ; i ++)
        {
            vtkIntArray* modelIdArray = m_modelIdArrayList[i];
            vtkIdTypeArray* idArray = m_idArrayList[i];

            if (!modelIdArray || !idArray)
            {
                continue;
            }

            for (int j = 0; j < modelIdArray->GetNumberOfValues(); j++)
            {
                int modelId = modelIdArray->GetValue(j);
                if (modelId == -1)
                {
                    idArray->InsertNextValue(j);
                }
                else
                {
                    if (modelVisibilityHash[modelId])
                    {
                        idArray->InsertNextValue(j);
                    }
                }
            }
        }

        // Update the data.
        for (vtkPolyData* polyData : m_polyDataList)
        {
            polyData->Modified();
        }

        // Save the flags.
        m_modelVisibilityHash = modelVisibilityHash;
    }

    void FITKGraphObjectBC::initSource()
    {
        // The arrow source.
        m_arrowSource = vtkArrowSource::New();
        m_arrowSource->SetShaftRadius(0);
        m_arrowSource->SetTipLength(0.25);
        m_arrowSource->SetTipRadius(0.125);
        m_arrowSource->SetTipResolution(m_sourceResolution);
        m_sourceList.push_back(m_arrowSource);

        // The cone source.
        m_coneSource = vtkConeSource::New();
        m_coneSource->SetHeight(0.25);
        m_coneSource->SetRadius(0.125);
        m_coneSource->SetResolution(m_sourceResolution);
        m_coneSource->SetCenter(- m_coneSource->GetHeight() * 0.5, 0, 0);
        m_sourceList.push_back(m_coneSource);

        // The poly data list for creating rotation BC.
        for (int i = 0; i < _DataCount; i++)
        {
            // Poly data.
            m_polyDataList.push_back(vtkPolyData::New());
            m_pointsList.push_back(vtkPoints::New());
            m_cellsList.push_back(vtkCellArray::New());
            m_scalarList.push_back(vtkDoubleArray::New());

            vtkDoubleArray* normal = vtkDoubleArray::New();
            normal->SetNumberOfComponents(3);
            m_normalList.push_back(normal);

            m_modelIdArrayList.push_back(vtkIntArray::New());
            m_idArrayList.push_back(vtkIdTypeArray::New());
        }

        // The arrow length.
        m_glyphSize = m_defaultSize * 0.8;
    }

    void FITKGraphObjectBC::getValue(int stepId)
    {
        // Initialize.
        for (int i = 0; i < 6; i++)
        {
            m_value[i] = 0;
            m_enableStates[i] = 0;
        }

        // Get the coodinate from the BC( If exist. ).
        m_coordinate = nullptr;

        bool isValid = false;

        // Displacement/Rotation or Velocity/Angular Velocity.
        Interface::FITKAbaBoundaryConditionDisplacement* dispBCData = dynamic_cast<Interface::FITKAbaBoundaryConditionDisplacement*>(_dataObj);
        if (dispBCData && !isValid)
        {
            // The BC value.
            m_value[0] = dispBCData->getU1(stepId);
            m_value[1] = dispBCData->getU2(stepId);
            m_value[2] = dispBCData->getU3(stepId);
            m_value[3] = dispBCData->getUR1(stepId);
            m_value[4] = dispBCData->getUR2(stepId);
            m_value[5] = dispBCData->getUR3(stepId);

            // The BC state.
            m_enableStates[0] = dispBCData->getU1State(stepId);
            m_enableStates[1] = dispBCData->getU2State(stepId);
            m_enableStates[2] = dispBCData->getU3State(stepId);
            m_enableStates[3] = dispBCData->getUR1State(stepId);
            m_enableStates[4] = dispBCData->getUR2State(stepId);
            m_enableStates[5] = dispBCData->getUR3State(stepId);

            m_coordinate = dispBCData->getCsysObj();

            isValid = true;
        }

        // Symmetry/Antisymmetry/Encastre.
        Interface::FITKAbaBoundaryConditionSymmetry* symBCData = dynamic_cast<Interface::FITKAbaBoundaryConditionSymmetry*>(_dataObj);
        if (symBCData && !isValid)
        {
            Interface::AbaSymmetryAntisymmetryEncastreType::SymmetryAntisymmetryEncastreType type = symBCData->getType();
            switch (type)
            {
            case Interface::AbaSymmetryAntisymmetryEncastreType::XSYMM:
                m_enableStates[0] = 1; m_enableStates[4] = 1; m_enableStates[5] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::YSYMM:
                m_enableStates[1] = 1; m_enableStates[3] = 1; m_enableStates[5] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::ZSYMM:
                m_enableStates[2] = 1; m_enableStates[3] = 1; m_enableStates[4] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::XASYMM:
                m_enableStates[1] = 1; m_enableStates[2] = 1; m_enableStates[3] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::YASYMM:
                m_enableStates[0] = 1; m_enableStates[2] = 1; m_enableStates[4] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::ZASYMM:
                m_enableStates[0] = 1; m_enableStates[1] = 1; m_enableStates[5] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::PINNED:
                m_enableStates[0] = 1; m_enableStates[1] = 1; m_enableStates[2] = 1;
                break;
            case Interface::AbaSymmetryAntisymmetryEncastreType::ENCASTRE:
                m_enableStates[0] = 1; m_enableStates[1] = 1; m_enableStates[2] = 1;
                m_enableStates[3] = 1; m_enableStates[4] = 1; m_enableStates[5] = 1;
                break;
            default:
                break;
            }

            m_coordinate = symBCData->getCsysObj();

            isValid = true;
        }

        // Get the direction from coodinate.
        if (m_coordinate)
        {
            // Save the coordinate ID.
            m_coordinateId = m_coordinate->getDataObjectID();
        }

        // Set the glyph direction by value.
        m_symbolDirecs_1[0] = qFuzzyCompare(m_value[0], 0) ? 0 : m_value[0] / fabs(m_value[0]);
        m_symbolDirecs_1[1] = qFuzzyCompare(m_value[1], 0) ? 0 : m_value[1] / fabs(m_value[1]);
        m_symbolDirecs_1[2] = qFuzzyCompare(m_value[2], 0) ? 0 : m_value[2] / fabs(m_value[2]);
        m_symbolDirecs_2[0] = qFuzzyCompare(m_value[3], 0) ? 0 : m_value[3] / fabs(m_value[3]);
        m_symbolDirecs_2[1] = qFuzzyCompare(m_value[4], 0) ? 0 : m_value[4] / fabs(m_value[4]);
        m_symbolDirecs_2[2] = qFuzzyCompare(m_value[5], 0) ? 0 : m_value[5] / fabs(m_value[5]);
    }

    void FITKGraphObjectBC::insertNextPoint(double* pos, int modelId, bool addToLoadPoints)
    {
        // For creating the poly data faster, the pointer check is closed, make sure
        // the following pointer are ALL NOT NULLPTR.
        // Save the points for refreshing actors.
        if (addToLoadPoints)
        {
            m_loadPoints->InsertNextPoint(pos); 
        }

        // Initialize the direction of the load.
        double direcXYZ[9]{ 1., 0., 0., 0., 1., 0., 0., 0., 1. };

        // Try to get the direction by coordinate transform.
        if (m_coordinate)
        {         
            m_coordinate->get3DirectionAtPoint(pos, direcXYZ);
        }

        // Add six points, cells, normal and scalar for x, y, z's double and single glyph BC poly data.
        insertNextSinglePoint(pos, modelId, direcXYZ, addToLoadPoints);
        insertNextDoublePoint(pos, modelId, direcXYZ, addToLoadPoints);
    }

    void FITKGraphObjectBC::insertNextPoint(vtkPoints* points, vtkCellArray* cells, double* pos)
    {
        int ptId = points->InsertNextPoint(pos);
        vtkSmartPointer<vtkVertex> vertex = vtkSmartPointer<vtkVertex>::New();
        vertex->GetPointIds()->SetId(0, ptId);
        cells->InsertNextCell(ptId);
    }

    void FITKGraphObjectBC::insertNextSinglePoint(double* pos, int modelId, double* direction, bool addToLoadPoints)
    {
        // X, Y, Z direction.
        double ptPos[3]{ pos[0], pos[1], pos[2] };
        for (int i = 0; i < 3; i++)
        {
            if (m_enableStates[i] == 1)
            {
                double normal[3]{ 0, 0, 0 };
                for (int j = 0; j < 3; j++)
                {
                    normal[j] = direction[j + 3 * i];

                    // Add the symbol to directions.
                    normal[j] = m_symbolDirecs_1[i] == 0 ? normal[j] : normal[j] * m_symbolDirecs_1[i];
                }

                if (!qFuzzyCompare(m_value[i], 0))
                {                  
                    insertNextPoint(m_pointsList[_NoneZeroSingle], m_cellsList[_NoneZeroSingle], ptPos);
                    m_normalList[_NoneZeroSingle]->InsertNextTuple(normal);
                    m_scalarList[_NoneZeroSingle]->InsertNextValue(1);

                    if (addToLoadPoints)
                    {
                        m_modelIdArrayList[_NoneZeroSingle]->InsertNextValue(modelId);
                    }
                }
                else
                {
                    insertNextPoint(m_pointsList[_ZeroSingle], m_cellsList[_ZeroSingle], ptPos);
                    m_normalList[_ZeroSingle]->InsertNextTuple(normal);
                    m_scalarList[_ZeroSingle]->InsertNextValue(1);

                    if (addToLoadPoints)
                    {
                        m_modelIdArrayList[_ZeroSingle]->InsertNextValue(modelId);
                    }
                }
            }
        }
    }

    void FITKGraphObjectBC::insertNextDoublePoint(double* pos, int modelId, double* direction, bool addToLoadPoints)
    {
        // X, Y, Z direction.
        double ptPos[3]{ pos[0], pos[1], pos[2] };
        for (int i = 3; i < 6; i++)
        {
            if (m_enableStates[i] == 1)
            {
                double normal[3]{ 0, 0, 0 };
                for (int j = 0; j < 3; j++)
                {
                    normal[j] = direction[j + 3 * (i - 3)];

                    // Add the symbol to directions.
                    normal[j] = m_symbolDirecs_2[i - 3] == 0 ? normal[j] : normal[j] * m_symbolDirecs_2[i - 3];
                }

                if (!qFuzzyCompare(m_value[i], 0))
                {
                    insertNextPoint(m_pointsList[_NoneZeroDouble], m_cellsList[_NoneZeroDouble], ptPos);
                    m_normalList[_NoneZeroDouble]->InsertNextTuple(normal);
                    m_scalarList[_NoneZeroDouble]->InsertNextValue(1);

                    if (addToLoadPoints)
                    {
                        m_modelIdArrayList[_NoneZeroDouble]->InsertNextValue(modelId);
                    }
                }
                else
                {
                    insertNextPoint(m_pointsList[_ZeroDouble], m_cellsList[_ZeroDouble], ptPos);
                    m_normalList[_ZeroDouble]->InsertNextTuple(normal);
                    m_scalarList[_ZeroDouble]->InsertNextValue(1);

                    if (addToLoadPoints)
                    {
                        m_modelIdArrayList[_ZeroDouble]->InsertNextValue(modelId);
                    }
                }
            }
        }
    }

    bool FITKGraphObjectBC::generatePolyData(Interface::FITKBoundaryLocation* bc)
    {
        // Clear the saved states.
        //@{
        for (vtkIntArray* modelIdArray : m_modelIdArrayList)
        {
            resetVtkObj(modelIdArray);
        }
        //@}

        return this->Superclass::generatePolyData(bc);
    }

    void FITKGraphObjectBC::generateGraph(vtkPolyDataAlgorithm* src, double size, QVariant details)
    {
        // Abandoned in this class, use 'generateGraphs' instead.
        Q_UNUSED(src); 
        Q_UNUSED(size); 
        Q_UNUSED(details);
    }

    void FITKGraphObjectBC::generateGraphs(double size)
    {
        // Get the real size value.
        size = size < 0 ? m_glyphSize : size;

        // Set the normals and scalars.
        for (int i = 0; i < _DataCount; i++)
        {
            m_polyDataList[i]->GetPointData()->SetNormals(m_normalList[i]);
            m_polyDataList[i]->GetPointData()->SetScalars(m_scalarList[i]);
            m_polyDataList[i]->SetPoints(m_pointsList[i]);
            m_polyDataList[i]->SetVerts(m_cellsList[i]);
        }

        for (int i = 0; i < _ActorCount; i++)
        {
            // Glyph.
            vtkGlyph3D* glyph = vtkGlyph3D::New();
            vtkDistanceToCamera* distanceToCamera = vtkDistanceToCamera::New();

            // Extractor.
            //@{
            vtkSelectionNode* node = vtkSelectionNode::New();
            node->SetFieldType(vtkSelectionNode::SelectionField::POINT);
            node->SetContentType(vtkSelectionNode::INDICES);
            m_selectNodeList.push_back(node);

            vtkSelection* selection = vtkSelection::New();
            selection->AddNode(node);
            m_sectionList.push_back(selection);   

            vtkExtractSelection* extractor = vtkExtractSelection::New();
            extractor->SetInputData(1, selection);
            m_extractSelectionList.push_back(extractor);
            //@}

            if (m_useMaskPoints)
            {
                // Initialize the mask points.
                vtkMaskPoints* maskPoints = vtkMaskPoints::New();
                FITKGraphCommons::InitializeMaskPoints(maskPoints, extractor->GetOutputPort(), m_maxSampleNumber);
                m_maskPointsList.push_back(maskPoints);

                distanceToCamera->SetInputConnection(maskPoints->GetOutputPort());
            }
            else
            {
                distanceToCamera->SetInputConnection(extractor->GetOutputPort());
            }

            distanceToCamera->SetScreenSize(size);
            glyph->SetInputConnection(distanceToCamera->GetOutputPort());

            // Add the point array name need to be used.      
#if VTK_MAJOR_VERSION < 8
            glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
            glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, distanceToCamera->GetDistanceArrayName());
#endif

            glyph->SetScaling(true);
            glyph->SetScaleModeToScaleByScalar();
            glyph->SetVectorModeToUseNormal();

            // Add to data list.
            m_glyphList.push_back(glyph);
            m_distToCameraList.push_back(distanceToCamera);

            // Create actor.
            FITKGraphActor* actor = FITKGraphActor::New();
            actor->setGraphObject(this);
            actor->SetPickable(false);
            actor->setInputConnection(glyph->GetOutputPort());
            addActor(actor);
            m_loadActorList.push_back(actor);
        }

        // Each level of smaller scale.
        double scaleDelta = 0.1;

        // Set the input data and source, and initialize actor's properties.
        //@{
        // Long arrow for displacement.
        m_selectNodeList[_NoneZeroSingle]->SetSelectionList(m_idArrayList[_NoneZeroSingle]);
        m_extractSelectionList[_NoneZeroSingle]->SetInputData(m_polyDataList[_NoneZeroSingle]);
        m_glyphList[_NoneZeroSingle]->SetSourceConnection(m_arrowSource->GetOutputPort());
        initActorProperties(m_loadActorList[_NoneZeroSingle], 1);

        // Shorter than displacement but the longer arrow for rotation.
        vtkArrowSource* arrowSource2 = vtkArrowSource::New();
        arrowSource2->SetShaftRadius(0);
        arrowSource2->SetTipLength(m_arrowSource->GetTipLength());
        arrowSource2->SetTipRadius(m_arrowSource->GetTipRadius() * (1. / (1 - scaleDelta)));
        arrowSource2->SetTipResolution(m_sourceResolution);
        m_sourceList.push_back(arrowSource2);

        m_selectNodeList[_NoneZeroDouble]->SetSelectionList(m_idArrayList[_NoneZeroDouble]);
        m_extractSelectionList[_NoneZeroDouble]->SetInputData(m_polyDataList[_NoneZeroDouble]);
        m_glyphList[_NoneZeroDouble]->SetSourceConnection(arrowSource2->GetOutputPort());
        m_distToCameraList[_NoneZeroDouble]->SetScreenSize(size * (1 - scaleDelta));
        initActorProperties(m_loadActorList[_NoneZeroDouble], 2);

        // The shortest arrow for rotation.
        vtkArrowSource* arrowSource3 = vtkArrowSource::New();
        arrowSource3->SetShaftRadius(0);
        arrowSource3->SetTipLength(m_arrowSource->GetTipLength());
        arrowSource3->SetTipRadius(m_arrowSource->GetTipRadius() * (1. / (1 - scaleDelta * 2)));
        arrowSource3->SetTipResolution(m_sourceResolution);
        m_sourceList.push_back(arrowSource3);

        m_selectNodeList[_NoneZeroDouble2]->SetSelectionList(m_idArrayList[_NoneZeroDouble]);
        m_extractSelectionList[_NoneZeroDouble2]->SetInputData(m_polyDataList[_NoneZeroDouble]);
        m_glyphList[_NoneZeroDouble2]->SetSourceConnection(arrowSource3->GetOutputPort());
        m_distToCameraList[_NoneZeroDouble2]->SetScreenSize(size * (1 - scaleDelta * 2));
        initActorProperties(m_loadActorList[_NoneZeroDouble2], 2);

        // Cone source for displacement.
        m_selectNodeList[_ZeroSingle]->SetSelectionList(m_idArrayList[_ZeroSingle]);
        m_extractSelectionList[_ZeroSingle]->SetInputData(m_polyDataList[_ZeroSingle]);
        m_glyphList[_ZeroSingle]->SetSourceConnection(m_coneSource->GetOutputPort());
        initActorProperties(m_loadActorList[_ZeroSingle], 1);

        // Cone source for rotation.
        vtkConeSource* coneSource2 = vtkConeSource::New();
        coneSource2->SetHeight(m_coneSource->GetHeight() * 1.5);
        coneSource2->SetRadius(m_coneSource->GetRadius());
        coneSource2->SetResolution(m_sourceResolution);
        coneSource2->SetCenter(- coneSource2->GetHeight() * 0.5, 0, 0);
        m_sourceList.push_back(coneSource2);

        m_selectNodeList[_ZeroDouble]->SetSelectionList(m_idArrayList[_ZeroDouble]);
        m_extractSelectionList[_ZeroDouble]->SetInputData(m_polyDataList[_ZeroDouble]);
        m_glyphList[_ZeroDouble]->SetSourceConnection(coneSource2->GetOutputPort());
        initActorProperties(m_loadActorList[_ZeroDouble], 2);

        // Cone source for rotation 2.
        vtkConeSource* coneSource3 = vtkConeSource::New();
        coneSource3->SetHeight(m_coneSource->GetHeight() * 1.5);
        coneSource3->SetRadius(m_coneSource->GetRadius());
        coneSource3->SetResolution(m_sourceResolution);
        coneSource3->SetCenter(- coneSource3->GetHeight(), 0, 0);
        m_sourceList.push_back(coneSource3);

        m_selectNodeList[_ZeroDouble2]->SetSelectionList(m_idArrayList[_ZeroDouble]);
        m_extractSelectionList[_ZeroDouble2]->SetInputData(m_polyDataList[_ZeroDouble]);
        m_glyphList[_ZeroDouble2]->SetSourceConnection(coneSource3->GetOutputPort());
        initActorProperties(m_loadActorList[_ZeroDouble2], 2);
        //@}

        showActors();
    }

    void FITKGraphObjectBC::initActorProperties(vtkProp* actor, QVariant details)
    {
        // Set model actor properties. Default show face and edge.
        FITKGraphActor* fActor = FITKGraphActor::SafeDownCast(actor);
        if (!fActor)
        {
            return;
        }

        // Set the scalar off.
        fActor->setScalarVisibility(false);
        fActor->GetProperty()->SetRepresentation(1);

        if (details == 1)
        {
            double color3[3] = { 0 ,0, 0 };
            FITKGraphCommons::QColorToDouble3(m_color_1, color3);
            fActor->GetProperty()->SetColor(color3);
        }
        else if (details == 2)
        {
            double color3[3] = { 0 ,0, 0 };
            FITKGraphCommons::QColorToDouble3(m_color_2, color3);
            fActor->GetProperty()->SetColor(color3);
        }
    }

    void FITKGraphObjectBC::showActors()
    {
        if (m_pointsList.count() < 4 || m_loadActorList.count() < 6)
        {
            return;
        }

        bool showNoneZeroSingle = m_pointsList[_NoneZeroSingle]->GetNumberOfPoints();
        m_loadActorList[_NoneZeroSingle]->SetVisibility(showNoneZeroSingle);

        bool showZeroSingle = m_pointsList[_ZeroSingle]->GetNumberOfPoints();
        m_loadActorList[_ZeroSingle]->SetVisibility(showZeroSingle);

        bool showNoneZeroDouble = m_pointsList[_NoneZeroDouble]->GetNumberOfPoints();
        m_loadActorList[_NoneZeroDouble]->SetVisibility(showNoneZeroDouble);
        m_loadActorList[_NoneZeroDouble2]->SetVisibility(showNoneZeroDouble);

        bool showZeroDouble = m_pointsList[_ZeroDouble]->GetNumberOfPoints();
        m_loadActorList[_ZeroDouble]->SetVisibility(showZeroDouble);
        m_loadActorList[_ZeroDouble2]->SetVisibility(showZeroDouble);
    }

    bool FITKGraphObjectBC::checkDataChanged()
    {
        // Get the current coodiante from force or moment.
        Interface::FITKCoordinateSystem* coordinate{ nullptr };

        // Displacement/Rotation.
        Interface::FITKAbaBoundaryConditionDisplacement* dispBCData = dynamic_cast<Interface::FITKAbaBoundaryConditionDisplacement*>(_dataObj);
        if (dispBCData)
        {
            coordinate = dispBCData->getCsysObj();
        }

        // Symmetry/Antisymmetry/Encastre.
        Interface::FITKAbaBoundaryConditionSymmetry* symBCData = dynamic_cast<Interface::FITKAbaBoundaryConditionSymmetry*>(_dataObj);
        if (symBCData)
        {
            coordinate = symBCData->getCsysObj();
        }

        int currentCoordId = -1;
        if (coordinate)
        {
            currentCoordId = coordinate->getDataObjectID();
        }

        // Check if the coordinate changed.
        return currentCoordId != m_coordinateId;
    }
}   // namespace Exchange